package req

import (
	"errors"
	"fmt"
	"project-info/src/lib"
	"project-info/src/model"
	"strings"

	"github.com/golang-jwt/jwt"
)

// JwtHandlerConfig jwt 中间件配置
func JwtHandlerConfig() lib.MiddlewareFunc {
	jwtConfig := struct {
		SigningMethod string
		AuthScheme    string
		TokenLookup   string
		GetKey        string
		SigningKey    []byte
		Claims        jwt.MapClaims
	}{
		SigningMethod: "HS256",
		AuthScheme:    "Bearer",
		TokenLookup:   "header:Authorization",
		GetKey:        lib.JwtContextKey,
		SigningKey:    []byte(model.JWT_SECRET),
		Claims: jwt.MapClaims{
			lib.ExpClaimsKey: model.JWT_EXPIRE,
		},
	}
	parts := strings.Split(jwtConfig.TokenLookup, ":")

	keyFunc := func(token *jwt.Token) (interface{}, error) {
		if token.Method.Alg() != jwtConfig.SigningMethod {
			return nil, fmt.Errorf("unexpected jwt signing method=%v", token.Header["alg"])
		}
		return jwtConfig.SigningKey, nil
	}

	// 从请求中获取token
	return func(c *lib.GinContext) error {
		// websocket 从query参数中获取token
		auth := ""
		if isWebSocketRequest(c) {
			tokenStirng := c.QueryParam("token")
			if tokenStirng == "" {
				return errors.New("token is empty")
			}
			auth = tokenStirng
		} else {
			tokenStirng, err := jwtFromHeader(c, parts[1], jwtConfig.AuthScheme)
			if err != nil {
				return err
			}
			auth = tokenStirng
		}

		token := new(jwt.Token)
		token, err := jwt.Parse(auth, keyFunc)
		if err != nil {
			return err
		}

		if err == nil && token.Valid {
			c.Set(jwtConfig.GetKey, token)
			return nil
		}
		return errors.New("invalid or expired jwt")
	}
}

// jwtFromHeader 从请求头中获取token
func jwtFromHeader(c *lib.GinContext, header string, authScheme string) (string, error) {
	auth := c.Request().Header.Get(header)
	l := len(authScheme)
	if len(auth) > l+1 && auth[:l] == authScheme {
		return auth[l+1:], nil
	}
	return "", errors.New("missing or malformed jwt")
}

// isWebSocketRequest 检查是否为WebSocket请求
func isWebSocketRequest(c *lib.GinContext) bool {
	return strings.ToLower(c.GetHeader("Connection")) == "upgrade" &&
		strings.ToLower(c.GetHeader("Upgrade")) == "websocket"
}
